package com.cyy.common.aspect;

import com.alibaba.fastjson2.JSON;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.MDC;
import org.springframework.core.NamedThreadLocal;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import java.util.*;

/**
 * 接口请求记录：只作为信息输出，不记录入库
 */
@Slf4j
@Aspect
@Component
public class WebLogAspect {

    private static final ThreadLocal<Long> START_TIME_THREAD_LOCAL = new NamedThreadLocal<>("ThreadLocal StartTime");

    @Pointcut("execution(public * com..controller.*.*(..))")
    public void webLog() {
    }

    @Before("webLog()")
    public void doBefore(JoinPoint joinPoint) throws Throwable {
        MDC.put("TRACE_ID", UUID.randomUUID().toString());
        long beginTime = System.currentTimeMillis();
        START_TIME_THREAD_LOCAL.set(beginTime);
        HttpServletRequest request = currentRequest();
        Map<String, String> headerInfo = getHeadersInfo(request);
        log.info(
                """

                        ================================>
                        ==>  URL: {}
                        ==>  HTTP_METHOD: {}
                        ==>  CLASS_METHOD: {}
                        ==>  SESSION_ID: {}
                        ==>  IP: {}
                        ==>  PARAM: {}
                        ==>  HEADER: {}
                        <================================"""
                , request.getRequestURI()
                , request.getMethod()
                , joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName()
                , request.getSession().getId()
                , request.getRemoteAddr()
                , JSON.toJSONString(joinPoint.getArgs())
                , JSON.toJSONString(headerInfo)
        );
    }

    @AfterReturning(returning = "ret", pointcut = "webLog()")
    public void doAfterReturning(Object ret) throws Throwable {

        long beginTime = START_TIME_THREAD_LOCAL.get();
        log.info(
                """

                        ================================>
                        ==>  耗时 : {},
                        ==>  RESPONSE : {}
                        <================================""",
                this.formatDateTime(System.currentTimeMillis() - beginTime),

                JSON.toJSONString(ret)
        );
        START_TIME_THREAD_LOCAL.remove();
        MDC.clear();
    }


    private Map<String, String> getHeadersInfo(HttpServletRequest request) {
        Map<String, String> map = new HashMap<>();

        Enumeration<String> headerNames = request.getHeaderNames();
        while (headerNames.hasMoreElements()) {
            String key = headerNames.nextElement();
            String value = request.getHeader(key);
            map.put(key, value);
        }
        return map;
    }

    private String formatDateTime(long timeMillis) {
        long day = timeMillis / (24 * 60 * 60 * 1000);
        long hour = (timeMillis / (60 * 60 * 1000) - day * 24);
        long min = ((timeMillis / (60 * 1000)) - day * 24 * 60 - hour * 60);
        long s = (timeMillis / 1000 - day * 24 * 60 * 60 - hour * 60 * 60 - min * 60);
        long sss = (timeMillis - day * 24 * 60 * 60 * 1000 - hour * 60 * 60 * 1000 - min * 60 * 1000 - s * 1000);
        return (day > 0 ? day + "," : "") + hour + ":" + min + ":" + s + "." + sss;
    }

    private HttpServletRequest currentRequest() {
        ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        return Optional.ofNullable(servletRequestAttributes).map(ServletRequestAttributes::getRequest).orElse(null);
    }

}
